grammar prop;


@header {
package compiler;

import graph.*;
import java.util.Map;
import java.util.HashMap;
import java.io.*;
}

@lexer::header { package compiler;}

@members {
//This is the main graph
Graph graph;

//Map which holds all variables declared and their type
Map<String,VariableNode> declaredVariables = new HashMap<String,VariableNode>();

//Override these two methods in order for ANTLR to stop parsing when in sees an error
@Override
public Object recoverFromMismatchedSet(IntStream input,RecognitionException e,BitSet follow) throws RecognitionException
{
	e.printStackTrace();
	throw e;
}

@Override
protected Object recoverFromMismatchedToken(IntStream input, int ttype, BitSet follow) throws RecognitionException
{
	throw new RecognitionException();
}
}

@rulecatch {
catch (RecognitionException e) {
throw e;
}
}

program	returns [Graph g]
@init
{
Node node = null;
}
@after{
$g = new Graph(node, declaredVariables);
System.out.println("Declared variables:");
System.out.println(declaredVariables.toString());
}
	:	n=statement {node = $n.node;}
	;
	
block returns [BlockNode node]
@init
{
$node = new BlockNode();
}
	:	'{' (n=statement
	{
		if($n.node!=null)
			$node.addStatementNode($n.node);
	})+ '}'
	;
		
statement returns [Node node]
	: 	n=expression ';' {$node = $n.node;}
	; 

parExpression returns [Node node]
    :   '(' n=expression ')'
	{ $node = $n.node;}
    ;
    
expression returns [Node node]
@init{Node lastNode;}
	:	n1=andExp {$node = $n1.node; lastNode = $n1.node;} 
		(OROP n2=andExp{
								BoolOperatorNode temp = new BoolOperatorNode($OROP.text);
								temp.setLeftOperand((ValueNode)lastNode);
								temp.setRightOperand((ValueNode)$n2.node);
								$node = temp;
								lastNode = $node;
							})*
	;

andExp returns [Node node]
@init{Node lastNode;}
	:	n1=compareExp {$node = $n1.node; lastNode = $n1.node;}
		(ANDOP n2=compareExp {
								BoolOperatorNode temp = new BoolOperatorNode($ANDOP.text);
								temp.setLeftOperand((ValueNode)lastNode);
								temp.setRightOperand((ValueNode)$n2.node);
								$node = temp;
								lastNode = $node;
							})*
	;

compareExp returns [Node node]
@init{Node lastNode;}
	:	n1=additiveExp {$node = $n1.node; lastNode = $n1.node;}
		(COMPOP n2=additiveExp{
								BoolOperatorNode temp = new BoolOperatorNode($COMPOP.text);
								temp.setLeftOperand((ValueNode)lastNode);
								temp.setRightOperand((ValueNode)$n2.node);
								$node = temp;
								lastNode = $node;
							})*
	;
	
additiveExp returns [Node node]
@init{Node lastNode;}
    :   n1=multiplicativeExp {$node = $n1.node; lastNode = $n1.node;}
		(ADDOP n2=multiplicativeExp{
								IntOperatorNode temp = new IntOperatorNode($ADDOP.text);
								temp.setLeftOperand((ValueNode)lastNode);
								temp.setRightOperand((ValueNode)$n2.node);
								$node = temp;
								lastNode = $node;
							})*
    ;

multiplicativeExp returns [Node node]
@init{Node lastNode;}
    :   n1=unaryExp {$node = $n1.node; lastNode = $n1.node;} 
		( MULTOP n2=unaryExp {
								IntOperatorNode temp = new IntOperatorNode($MULTOP.text);
								temp.setLeftOperand((ValueNode)lastNode);
								temp.setRightOperand((ValueNode)$n2.node);
								$node = temp;
								lastNode = $node;
							})*
    ;
    
unaryExp returns [Node node]
    : UNARYOP? n=primary
		{
			//Convert a unrary operator to a binary operator
			if($UNARYOP==null)
				$node = $n.node;
			else if($UNARYOP.text.equals("+"))
			{
				IntOperatorNode temp = new IntOperatorNode("+");
				temp.setLeftOperand(new IntLiteralNode(0));
				temp.setRightOperand($n.node);
				$node = temp;
			}
			else if($UNARYOP.text.equals("-"))
			{
				IntOperatorNode temp = new IntOperatorNode("-");
				temp.setLeftOperand(new IntLiteralNode(0));
				temp.setRightOperand($n.node);
				$node = temp;
			}
			else if($UNARYOP.text.equals("!"))
			{
				BoolOperatorNode temp = new BoolOperatorNode("x|");
				temp.setLeftOperand(new BoolLiteralNode(true));
				temp.setRightOperand($n.node);
				$node = temp;
			}

		}
    ;

primary returns [ValueNode node]
	:	n=parExpression 
		{
			$node = (ValueNode)$n.node;
		}
	|	n=literal 
		{
			$node = (ValueNode)$n.node;
		}
	|	ID 		
		{
			//if the variable is used for the first time, add it
			if(!declaredVariables.containsKey($ID.text))
			{
				IntVariableNode nodeID = new IntVariableNode($ID.text);
				declaredVariables.put($ID.text,nodeID); 
			}
			
			//return the node
			$node = declaredVariables.get($ID.text);
			
		}
	;

literal returns [Node node]
	:	INT
		{
			$node = new IntLiteralNode(Integer.parseInt($INT.text));
		}
	|	BOOLEAN
		{
			$node = new BoolLiteralNode(Boolean.parseBoolean($BOOLEAN.text));
		}
	;
	
MULTOP : '*' | '/' | '%';

ADDOP : '+' | '-';

COMPOP : '==' | '!=' | '<' | '>';

ANDOP : '&&';

OROP : '||' | 'x|';

UNARYOP : '!' | '-' | '+';

BOOLEAN	:	'true'
	|	'false'
	;
	
TYPE 	:	'int' | 'bool'
	;
	
ID  :	('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
    ;

INT :	'0'..'9'+
    ;

COMMENT
    :   '//' ~('\n'|'\r')* '\r'? '\n' {$channel=HIDDEN;}
    |   '/*' ( options {greedy=false;} : . )* '*/' {$channel=HIDDEN;}
    ;

WS  :   ( ' '
        | '\t'
        | '\r'
        | '\n'
        ) {$channel=HIDDEN;}
    ;